#ifndef MATRIX_CLASS
#define MATRIX_CLASS

#include <iostream>
#include <vector>

#include "d_except.h"

using namespace std;

template <typename T>
class matrix
{
public:
    matrix(int numRows = 1, int numCols = 1, const T& initVal = T());
    // constructor.
    // Postcondition: create array having numRows x numCols elements
    // all of whose elements have value initVal

    vector<T>& operator[] (int i);
    // index operator.
    // Precondition: 0 <= i < nRows. a violation of this
    // precondition throws the indexRangeError exception.
    // Postcondition: if the operator is used on the left-hand
    // side of an assignment statement, an element of row i 
    // is changed

    const vector<T>& operator[](int i) const;
    // version for constant objects

    int rows() const;
    // return number of rows
    int cols() const;
    // return number of columns

    void resize(int numRows, int numCols);
    // modify the matrix size.
    // Postcondition: the matrix has size numRows x numCols.
    // any new elements are filled with the default value of type T

private:
    int nRows, nCols;
    // number of rows and columns

    vector<vector<T> > mat;
    // matrix is implemented as nRows vectors (rows),
    // each having nCols elements (columns)
};

template <typename T>
matrix<T>::matrix(int numRows, int numCols, const T& initVal):
	nRows(numRows), nCols(numCols),
	mat(numRows, vector<T>(numCols,initVal))
{}

// non-constant version. provides general access to matrix
// elements
template <typename T>
vector<T>& matrix<T>::operator[] (int i)
{
    if (i < 0 || i >= nRows)
        throw indexRangeError(
                "matrix: invalid row index", i, nRows);

    return mat[i];
}

// constant version.  can be used with a constant object.
// does not allow modification of a matrix element
template <typename T>
const vector<T>& matrix<T>::operator[] (int i) const
{
    if (i < 0 || i >= nRows)
        throw indexRangeError(
                "matrix: invalid row index", i, nRows);

    return mat[i];
}

template <typename T>
int matrix<T>::rows() const
{
    return nRows;
}

template <typename T>
int matrix<T>::cols() const
{
    return nCols;
}

template <typename T>
void matrix<T>::resize(int numRows, int numCols)
{
    int i;

    // handle case of no size change with a return
    if (numRows == nRows && numCols == nCols)
        return;

    // assign the new matrix size
    nRows = numRows;
    nCols = numCols;

    // resize to nRows rows
    mat.resize(nRows);

    // resize each row to have nCols columns
    for (i=0; i < nRows; i++)
        mat[i].resize(nCols);
}

#endif	// MATRIX_CLASS
